home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Celestin Apprentice 5
/
Apprentice-Release5.iso
/
Source Code
/
Libraries
/
DCLAP 6d
/
dclap6d
/
network
/
ncsasock
/
socket.c
< prev
next >
Wrap
Text File
|
1996-07-05
|
42KB
|
1,717 lines
/*
* BSD-style socket emulation library for the Mac
* Original author: Tom Milligan
* Current author: Charlie Reiman - creiman@ncsa.uiuc.edu
*
* This source file is placed in the public domian.
* Any resemblance to NCSA Telnet, living or dead, is purely coincidental.
*
* National Center for Supercomputing Applications
* 152 Computing Applications Building
* 605 E. Springfield Ave.
* Champaign, IL 61820
*/
/*
*
* The following calls are implemented
*
* socket
* bind
* listen
* accept
* connect
* read
* recv
* recvfrom
* write
* writev
* send
* sendto
* select
* close
* getdtablesize
* getsockname
* getpeername
* shutdown
* fcntl(F_DUPFD)
* fcntl(F_GETFL)
* fcntl(F_SETFL,FNDELAY)
* dup
* dup2
* ioctl(FIONBIO)
* ioctl(FIONREAD)
*
* Non-blocking I/O is supported. All calls which would block return
* immediately with an 'error' indicating so. Select() may be used to
* determine when an operation can be performed.
*
* Ioctl(FIONBIO) or fcntl(F_SETFL,FNDELAY) can be used to toggle or set
* the blocking status of a socket.
*
* In a blocking situation, accept() and read() return EWOULDBLOCK and
* refuse to do anything. Select() for read() to learn when a incoming
* connection is available.
*
* Connect() and write() (which shouldn't take too long anyway) start
* the operation and return EINPROGRESS. Select for write() to learn
* when connect() has completed. Write() on a socket which is still
* 'inprogress' return EALREADY.
*
*
* Socket operations are single threaded and half-duplex. Fixing this is
* left as an execise for the reader. Shouldn't be a terrible problem
* anyway. Read() never blocks and write() only blocks for long if there
* is a problem.
*
* Calls which find the socket busy will return EALREADY. These are
* read() or write() with a connect() or write() in progress.
*
*
* Socket options are not supported. Hence no setsockopt() and getsockopt()
* calls.
*
*
* CR: I attempted to support OOB data for send and recv, but not promises are
* made as I didn't have any immediate tests for them.
*
*
* Readv() is not implemented.
*
*
* All calls which encounter an error will set the global variable errno
* to indicate the problem. Some common values for errno are...
*
* EBADF the socket parameter is not a valid socket descriptor.
*
* EFAULT a pointer parameters is rubbish.
*
* EINVAL a non-pointer parameters is rubbish.
*
* ENOTCONN the socket should be in a connected state for this
* operation, but isn't.
*
* EISCONN the socket is already connected.
*
* ----------------- non-blocking I/O
*
* EWOULDBLOCK accept() or one of the read() calls would block.
*
* EINPROGRESS connect() or one of the write() operations has been
* started.
*
* ----------------- SINGLE THREAD
*
* EALREADY an operation is already in progress on the socket.
*
* ----------------
*
* EBUG an internal error occured.
*/
#ifdef USEDUMP
# pragma load "socket.dump"
#else
# include <Events.h>
# include <Types.h>
# include <Stdio.h>
# include <s_types.h>
# include <neti_in.h>
#ifdef COMP_CODEWAR
#define NOWAY
#undef EDOM
#undef ERANGE
#endif
# include <neterrno.h>
# include <s_file.h>
# include <s_ioctl.h>
# include <s_socket.h>
# include <s_time.h>
# include <s_uio.h>
# include "sock_str.h"
# include "sock_int.h"
#include "unixlib.h"
#endif
/* #include "sock_ext.h" */
#ifdef NOT_NOW_COMP_CODEWAR
#define EINVAL 22 /* Invalid argument */
#define EBADF 9 /* Bad file descriptor */
#define EMFILE 24 /* Too many open files */
#endif
/*
* GET YOUR GLOBALS HERE!
*/
int defaultSpin(spin_msg msg,long param);
SocketPtr sockets = NULL; /* The socket table. */
AllPb *pbList = NULL; /* The pb array */
short pbLast = 0; /* last pb used */
StreamHashEntPtr streams = NULL; /* The streams hash table */
SpinFn spinroutine = (SpinFn) defaultSpin; /* The spin routine. */
/*
* s_socket(domain, type, protocol)
*
* socket creates a MacTCP socket and returns a descriptor.
*
* Domain must be AF_INET
*
* Type may be SOCK_STREAM to create a TCP socket or
* SOCK_DGRAM to create a UDP socket.
*
* Protocol is ignored. (isn't it always?)
*
* TCP sockets provide sequenced, reliable, two-way connection
* based byte streams.
*
* A TCP socket must be in a connected
* state before any data may be sent or received on it. A
* connection to another socket is created with a connect() call
* or the listen() and accept() calls.
* Once connected, data may be transferred using read() and
* write() calls or some variant of the send() and recv()
* calls. When a session has been completed a close() may be
* performed.
*
*
* A UDP socket supports the exchange of datagrams (connectionless,
* unreliable messages of a fixed maximum length) with
* correspondents named in send() calls. Datagrams are
* generally received with recv(), which returns the next
* datagram with its return address.
*
* An fcntl() or ioctl() call can be used to enable non-blocking I/O.
*
* The return value is a descriptor referencing the socket or -1
* if an error occurs, in which case global variable errno is
* set to one of:
*
* ENOMEM Failed to allocate memory for the socket
* data structures.
*
* EAFNOSUPPORT Domain wasn't AF_INET.
*
* ESOCKTNOSUPPORT Type wasn't SOCK_STREAM or SOCK_DGRAM.
*
* EMFILE The socket descriptor table is full.
*/
int s_socket(
Int4 domain,
Int4 type,
short protocol)
{
SocketPtr sp;
int s;
#if SOCK_DEBUG >= 3
dprintf("s_socket:\n");
#endif
sock_init();
/*
* Support only Internet family
*/
if (domain != AF_INET)
return(sock_err(EAFNOSUPPORT));
switch(type)
{
case SOCK_DGRAM:
protocol = IPPROTO_UDP;
break;
case SOCK_STREAM:
protocol = IPPROTO_TCP;
break;
default:
return(sock_err(ESOCKTNOSUPPORT));
}
/*
* Create a socket table entry
*/
s = sock_free_fd(0); /* Get next free file descriptor */
if (s == -1)
return(sock_err(EMFILE));
sp = sockets+s;
sp->fd = s;
sp->protocol = protocol;
bzero(&sp->sa, sizeof(struct sockaddr_in));
bzero(&sp->peer, sizeof(struct sockaddr_in));
sp->sa.sin_family = AF_INET;
sp->sa.sin_len = sizeof(struct sockaddr_in);
sp->status = SOCK_STATUS_USED;
sp->nonblocking = false;
/* Create a new stream on the socket. */
switch(sp->protocol)
{
case IPPROTO_UDP:
/* udp streams are not created until the last minute */
/* because we dont know if the caller wants to assign */
/* a special port number yet (via bind) and mactcp */
/* assigns udp port numbers at stream creation */
sp->sstate = SOCK_STATE_NO_STREAM;
break;
case IPPROTO_TCP:
/* the tcp stream is created now because tcp port numbers */
/* are assigned during active/passiveOpen which is done */
/* during listen or connect */
sp->sstate = SOCK_STATE_UNCONNECTED;
if (sock_tcp_new_stream(sp) < 0)
return(-1); /* sock_err already called */
}
return(s);
}
/*
* s_bind(s, name, namelen)
*
* bind requests that the name (ip address and port) pointed to by
* name be assigned to the socket s.
*
* The return value is 0 on success or -1 if an error occurs,
* in which case global variable errno is set to one of:
*
* EAFNOSUPPORT The address family in name is not AF_INET.
*
* EINVAL The socket is already bound to an address.
*
* EADDRNOTAVAIL The specified address is not available
* from the local machine. ie. the address
* portion of name was not this machine's address.
*
* MacTCP does not separate name binding and connection establishment.
* Therefore the port number is not verified, just stored for later use.
*
* If a specific local port is not required, bind is optional in this
* implementation.
*/
int s_bind(
Int4 s,
struct sockaddr *sa_name,
Int4 namelen)
{
SocketPtr sp;
struct sockaddr_in *name=(struct sockaddr_in *)sa_name;
#if SOCK_DEBUG >= 3
dprintf("s_bind: bind %d to %08x/%d\n",
s, name->sin_addr.s_addr, name->sin_port);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
sp = sockets+s;
if (namelen < sizeof(struct sockaddr_in))
return(sock_err(EINVAL));
if (!goodptr(name))
return(sock_err(EFAULT));
if (name->sin_family != AF_INET)
return(sock_err(EAFNOSUPPORT));
if (sp->sa.sin_port != 0) /* already bound */
return(sock_err(EINVAL));
/*
* If client passed a local IP address, assure it is the right one
*/
if (name->sin_addr.s_addr != 0)
{
if (name->sin_addr.s_addr != xIPAddr())
return(sock_err(EADDRNOTAVAIL));
}
/*
* NOTE: can't check a TCP port for EADDRINUSE
* just save the address and port away for connect or listen or...
*/
sp->sa.sin_addr.s_addr = name->sin_addr.s_addr;
sp->sa.sin_port = name->sin_port;
return(0);
}
/*
* s_connect - initiate a connection on a MacTCP socket
*
* If the parameter s is a UDP socket,
* then this call specifies the address to which datagrams
* are to be sent, and the only address from which datagrams
* are to be received.
*
* If it is a TCP socket, then this call attempts to make a
* connection to another socket. The other socket is specified
* by an internet address and port.
*
* TCP sockets may successfully connect() only once;
*
* UDP sockets may use connect() multiple times to change
* their association. UDP sockets may dissolve the association
* by connecting to an invalid address, such as a null
* address.
*
* If the connection or binding succeeds, then 0 is returned.
* Otherwise a -1 is returned, and a more specific error code
* is stored in errno.
*
* EAFNOSUPPORT The address family in addr is not AF_INET.
*
* EHOSTUNREACH The TCP connection came up half-way and
* then failed.
*
* -------------- some day instead of EHOSTUNREACH -----------------
*
* EADDRNOTAVAIL The specified address is not available
* on the remote machine.
*
* ETIMEDOUT Connection establishment timed out
* without establishing a connection.
*
* ECONNREFUSED The attempt to connect was forcefully
* rejected.
*
* ENETUNREACH The network is not reachable from here.
*
* EHOSTUNREACH The host is not reachable from here.
*
* EADDRINUSE The address is already in use.
*/
int s_connect(
Int4 s,
struct sockaddr *sa_addr,
Int4 addrlen)
{
SocketPtr sp;
struct sockaddr_in *addr=(struct sockaddr_in *)sa_addr;
#if SOCK_DEBUG >= 2
dprintf("s_connect: connect %d to %08x/%d\n",
s, addr->sin_addr.s_addr, addr->sin_port);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
if (addrlen != sizeof(struct sockaddr_in))
return(sock_err(EINVAL));
if (! goodptr(addr))
return(sock_err(EFAULT));
if (addr->sin_family != AF_INET)
return(sock_err(EAFNOSUPPORT));
sp = sockets+s;
switch(sp->protocol)
{
case IPPROTO_UDP:
return(sock_udp_connect(sp,addr));
case IPPROTO_TCP:
return(sock_tcp_connect(sp,addr));
}
return(0);
}
/*
* s_listen()
*
* To accept connections, a socket is first created with
* socket(), a backlog for incoming connections is specified
* with listen() and then the connections are accepted with
* accept(). The listen() call applies only to TCP sockets.
*
* The qlen parameter is supposed to define the maximum length
* the queue of pending connections may grow to. It is ignored.
*
* A 0 return value indicates success; -1 indicates an error.
*
* EOPNOTSUPP s is not a TCP socket.
*/
int s_listen(
Int4 s,
Int4 qlen)
{
#pragma unused(qlen)
SocketPtr sp;
#if SOCK_DEBUG >= 2
dprintf("listen: listen %d\n", s);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
sp = sockets+s;
switch(sp->protocol)
{
case IPPROTO_UDP:
return(sock_err(EOPNOTSUPP));
case IPPROTO_TCP:
return(sock_tcp_listen(sp));
}
return(0);
}
/*
* s_accept(s, addr, addrlen)
*
* s is a socket that has been created with socket, bind, listen.
*
* Accept extracts the first connection on the queue of pending
* connections, creates a new socket with the same properties of s
* and allocates a new file descriptor, ns, for the socket.
*
* If no pending connections are present on the queue, and the socket
* is not marked as non-blocking, accept blocks the caller until
* a connection is present.
*
* If the socket is marked non-blocking and no pending connections
* are present on the queue, accept returns an error EWOULDBLOCK.
*
* The accepted socket, ns, is used to read and write data to and
* from the socket which connected to this one; it is not used
* to accept more connections. The original socket s remains
* open for accepting further connections.
*
* The argument addr is a result parameter that is filled in
* with the address of the connecting socket. The addrlen is
* a value-result parameter; it should initially contain the
* amount of space pointed to by addr; on return it will contain
* the actual length (in bytes) of the address returned.
*
* This call is used with TCP sockets only.
*
* It is possible to select a socket for the purposes of
* doing an accept by selecting it for read.
*
* Translation: To check and see if there is a connection pending,
* call s_select and check for pending reads.
*
* The call returns -1 on error. If it succeeds, it returns a
* non-negative integer that is a descriptor for the accepted
* socket.
*
* EOPNOTSUPP s is not a TCP socket.
*
* EMFILE The socket descriptor table is full.
*/
int s_accept(
Int4 s,
struct sockaddr *sa_addr,
Int4 *addrlen)
{
SocketPtr sp;
struct sockaddr_in *addr=(struct sockaddr_in *)sa_addr;
#if SOCK_DEBUG >= 2
dprintf("s_accept: %d\n", s);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
if (!goodptr(addr) || !goodptr(addrlen))
return(sock_err(EFAULT));
if (*addrlen < 0)
return(sock_err(EINVAL));
if (sock_free_fd(0) == -1)
return(sock_err(EMFILE));
sp = sockets+s;
switch(sp->protocol)
{
case IPPROTO_UDP:
return(sock_err(EOPNOTSUPP));
case IPPROTO_TCP:
return(sock_tcp_accept(sp,addr,addrlen));
}
return(sock_err(EFAULT));
}
/*
* s_accept_once
*
* A mac specific routine, designed to compenstate for a bug
* in MacTCP. If you close a passive, unconnected stream,
* MacTCP will generate an error. s_accept always creates
* a new listening (passive open) stream that will eventually
* need to be closed. s_accept_once does not create a new
* listening socket. It will return the same socket originally
* passed to it, and NO more connections will be accepted
* on the old listening port.
*
* Other than that, it is identical to s_accept.
*/
int s_accept_once(
Int4 s,
struct sockaddr *sa_addr,
Int4 *addrlen)
{
SocketPtr sp;
struct sockaddr_in *addr=(struct sockaddr_in *)sa_addr;
#if SOCK_DEBUG >= 2
dprintf("s_accept: %d\n", s);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
if (!goodptr(addr) || !goodptr(addrlen))
return(sock_err(EFAULT));
if (*addrlen < 0)
return(sock_err(EINVAL));
if (sock_free_fd(0) == -1)
return(sock_err(EMFILE));
sp = sockets+s;
switch(sp->protocol)
{
case IPPROTO_UDP:
return(sock_err(EOPNOTSUPP));
case IPPROTO_TCP:
{
int returnCode;
returnCode=sock_tcp_accept_once(sp,addr,addrlen);
return (returnCode ? returnCode : s );
}
}
return(sock_err(EFAULT));
}
/*
* s_close(s)
*
* The close call destroys the socket s. If this is the last reference
* to the underlying MacTCP stream, then the stream will be released.
*
* A 0 return value indicates success; -1 indicates an error.
*
* NOTE: if non-blocking I/O is enabled EWOULDBLOCK will be returned
* if there are TCP writes in progress. (UDP writes are
* performed synchronously.)
*
* All reads are terminated and unread data is lost.
*/
int s_close(
Int4 s)
{
int t;
SocketPtr sp;
int status;
#if SOCK_DEBUG >= 2
dprintf("s_close: %d\n", s);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
sp = sockets+s;
/*
* Look for duplicates of the socket. Only close down the connection
* if there are no duplicates(i.e. socket not dup'd).
*/
for (t = 0; t < NUM_SOCKETS; t++)
{
if (t == s)
continue;
if (!(sockets[t].status & SOCK_STATUS_USED))
continue;
else if (sockets[t].protocol == sp->protocol && sockets[t].stream == sp->stream )
{ /* found a duplicate */
#if SOCK_DEBUG >= 3
dprintf("s_close: found a dup at %d(%d, don't close stream\n",
t, sockets[t].protocol);
#endif
sock_clear_fd(s);
return(0);
}
}
/*
* No duplicates, close the stream.
*/
switch(sp->protocol)
{
case IPPROTO_UDP:
status = sock_udp_close(sp);
break;
case IPPROTO_TCP:
status = sock_tcp_close(sp);
break;
}
if (status != EWOULDBLOCK)
sock_clear_fd(s);
return(status);
}
/*
* s_read(s, buffer, buflen)
*
* s_recv(s, buffer, buflen, flags)
*
* s_recvfrom(s, buffer, buflen, flags, from, fromlen)
*
* read() attempts to read nbytes of data from the socket s.
*
* recv() and recvfrom() attempt to receive a message (ie a datagram)
* on the socket s.
*
* from returns the address of the socket which sent the message.
* fromlen is the usual value-result length parameter.
*
* Typically, read() is used with a TCP stream and recv() with
* UDP where the idea of a message makes more sense. But in fact,
* read() and recv() are equivalent.
*
* For UDP...
* If a message (ie. datagram) is too long to fit in the supplied
* buffer, excess bytes will be discarded..
*
* If no messages are available at the socket, the receive call
* waits for a message to arrive, unless the socket is non-
* blocking in which case -1 is returned with errno set to
* EWOULDBLOCK.
*
* For TCP...
* Regardless of non-blocking status, if less data is available
* than has been requested, only that much data is returned.
*
* If the socket is marked for non-blocking I/O, and the socket
* is empty, the operation will fail with the error EWOULDBLOCK.
* Otherwise, the operation will block until data is available
* or an error occurs.
*
* A return value of zero indicates that the stream has been
* closed and all data has already been read. ie. end-of-file.
*
* Flags is ignored.
*
* If successful, the number of bytes actually received is
* returned. Otherwise, a -1 is returned and the global variable
* errno is set to indicate the error.
*
* ESHUTDOWN The socket has been shutdown for receive operations.
*/
int s_recvfrom(Int4 s, void *buffer, Int4 buflen, Int4 flags,
struct sockaddr *sa_from, Int4 *fromlen);
int s_read(
Int4 s,
void *buffer,
Int4 buflen)
{
Int4 fromlen = 0;
return(s_recvfrom(s, buffer, buflen, 0, NULL, &fromlen));
}
int s_recv(
Int4 s,
void *buffer,
Int4 buflen,
Int4 flags)
{
Int4 fromlen = 0;
return(s_recvfrom(s, buffer, buflen, flags, NULL, &fromlen));
}
int s_recvfrom(
Int4 s,
void *buffer,
Int4 buflen,
Int4 flags,
struct sockaddr *sa_from,
Int4 *fromlen)
{
SocketPtr sp;
struct sockaddr_in *from=(struct sockaddr_in *)sa_from;
#if SOCK_DEBUG >= 3
dprintf ("s_recvfrom: %d\n", s);
#endif
if (s < 0 || s >= NUM_SOCKETS)
return (sock_err (EBADF));
sp = sockets+s;
if (! is_used (sp))
return (sock_err (EBADF));
if (!goodptr(buffer))
return (sock_err (EFAULT));
if (buflen <= 0)
return(sock_err(EINVAL));
if (! (from == NULL || goodptr(from)) )
return(sock_err(EFAULT));
if (! (fromlen == NULL || goodptr(fromlen)) )
return(sock_err(EFAULT));
else
if ( *fromlen < 0 )
return(sock_err(EINVAL));
if (sock_noread(sp))
return(sock_err(ESHUTDOWN));
switch(sp->protocol)
{
case IPPROTO_UDP:
return(sock_udp_recv(sp,(char*) buffer, buflen, flags, from, (int *)fromlen));
case IPPROTO_TCP:
return(sock_tcp_recv(sp,(char*) buffer, buflen, flags));
}
return(sock_err(EFAULT));
}
/*
* s_write(s, buffer, buflen)
*
* s_writev(s, iov, iovcnt)
*
* s_send(s, buffer, buflen, flags)
*
* s_sendto(s, buffer, buflen, flags, to, tolen)
*
* write() attempts to write nbytes of data to the socket s from
* the buffer pointed to by buffer.
*
* writev() gathers the output data from the buffers specified
* by the members of the iov array. Each iovec entry specifies
* the base address and length of an area in memory from which
* data should be written.
*
* send() and sendto() are used to transmit a message to another
* socket on the socket s.
*
* Typically, write() is used with a TCP stream and send() with
* UDP where the idea of a message makes more sense. But in fact,
* write() and send() are equivalent.
*
* For UDP...
*
* Write() and send() operations are completed as soon as the
* data is placed on the transmission queue.???????
*
* The address of the target is given by to.
*
* The message must be short enough to fit into one datagram.
*
* Buffer space must be available to hold the message to be
* transmitted, regardless of its non-blocking I/O state.
*
* For TCP...
* Write() and send() operations are not considered complete
* until all data has been sent and acknowledged.
*
* If a socket is marked for non-blocking I/O, the operation
* will return an 'error' of EINPROGRESS.
*
* If the socket is not marked for non-blocking I/O, the write will
* block until space becomes available.
*
* write() and send() may be used only when the socket is in a connected
* state, sendto() may be used at any time.
*
* Flags is ignored.
*
* These calls return the number of bytes sent, or -1 if an error
* occurred.
*
* EINVAL The sum of the iov_len values in the iov array was
* greater than 65535 (TCP) or 65507 (UDP) or there
* were too many entries in the array (16 for TCP or
* 6 for UDP).
*
* ESHUTDOWN The socket has been shutdown for send operations.
*
* EMSGSIZE The message is too big to send in one datagram. (UDP)
*
* ENOBUFS The transmit queue is full. (UDP)
*/
int s_really_send(
Int4 s,
void *buffer,
Int4 count,
Int4 flags,
struct sockaddr_in *to);
int s_write(
Int4 s,
void *buffer,
Int4 buflen)
{
return(s_really_send(s, buffer, buflen, 0, NULL));
}
int s_writev(
Int4 s,
struct iovec *iov,
Int4 count)
{
int result,tally=0;
while (count--)
{
if ( !goodptr( iov ) )
return sock_err(EFAULT);
result= s_really_send(s, (char *)(iov->iov_base),
(int)(iov->iov_len), 0, NULL);
if (result < 0)
return sock_err(result);
iov++;
tally+=result;
}
return tally;
}
int s_send(
Int4 s,
void *buffer,
Int4 buflen,
Int4 flags)
{
return(s_really_send(s, buffer, buflen, flags, NULL));
}
int s_sendto (
Int4 s,
void *buffer,
Int4 buflen,
Int4 flags,
struct sockaddr *sa_to,
Int4 tolen)
{
SocketPtr sp;
struct sockaddr_in *to=(struct sockaddr_in *)sa_to;
if (s < 0 || s >= NUM_SOCKETS)
return (sock_err (EBADF));
sp = sockets+s;
if (! is_used (sp))
return (sock_err (EBADF));
if (!goodptr(buffer))
return (sock_err (EFAULT));
if (buflen <= 0)
return(sock_err(EINVAL));
if (to != NULL && !goodptr(to))
return(sock_err(EFAULT));
if (to != NULL && tolen < sizeof(struct sockaddr_in))
return(sock_err(EINVAL));
return(s_really_send(s, buffer, buflen, flags, to));
}
int s_sendmsg(Int4 s,struct msghdr *msg,Int4 flags) {
SocketPtr sp;
struct iovec *iov=NULL;
int tally=0,result,count;
if (s < 0 || s >= NUM_SOCKETS)
return (sock_err (EBADF));
sp = sockets+s;
if (! is_used (sp))
return (sock_err (EBADF));
if (!goodptr(msg))
return (sock_err (EFAULT));
if ( msg->msg_name != NULL && !goodptr(msg->msg_name) )
return (sock_err (EFAULT));
if ( msg->msg_name != NULL && msg->msg_namelen < sizeof (struct sockaddr_in))
return (sock_err (EFAULT));
count = msg->msg_iovlen;
iov = msg->msg_iov;
while ( count -- ) {
if ( !goodptr( iov ) )
return sock_err(EFAULT);
result= s_really_send(s, (char *)(iov->iov_base),
(int)(iov->iov_len), flags,(struct sockaddr_in *)msg->msg_name);
if (result < 0)
return (sock_err(result));
iov++;
tally+=result;
}
return tally;
}
int s_really_send(
Int4 s,
void *buffer,
Int4 count,
Int4 flags,
struct sockaddr_in *to)
{
SocketPtr sp;
int tally=0;
#if SOCK_DEBUG >= 2
dprintf("s_really_send: %d %d bytes", s, count);
if (to != NULL)
dprintf(" to %08x/%d",to->sin_addr.s_addr,to->sin_port);
dprintf("\n");
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
sp = sockets+s;
#if SOCK_DEBUG >= 2
dprintf("state %d\n",sp->sstate);
dprintf("peer %08x/%d\n",sp->peer.sin_addr.s_addr,sp->peer.sin_port);
#endif
if (sock_nowrite(sp))
return(sock_err(ESHUTDOWN));
switch(sp->protocol)
{
case IPPROTO_UDP:
if (to == NULL && sp->sstate != SOCK_STATE_CONNECTED)
return(sock_err(ENOTCONN));
return(sock_udp_send(sp, to,(char*) buffer, count, flags));
break;
case IPPROTO_TCP:
if (to != NULL) /* sendto */
return(sock_err(EOPNOTSUPP));
if (sp->sstate != SOCK_STATE_CONNECTED)
return(sock_err(ENOTCONN));
return ( sock_tcp_send(sp, (char*) buffer, count,0 ));
break;
}
return(sock_err(ESHUTDOWN));
}
/*
* s_select(width, readfds, writefds, exceptfds, timeout)
*
* select() examines the I/O descriptor sets whose addresses
* are passed in readfds, writefds, and exceptfds to see if
* some of their descriptors are ready for reading, ready for
* writing, or have an exceptional condition pending. width is
* the number of bits to be checked in each bit mask that
* represent a file descriptor; the descriptors from 0 through
* width-1 in the descriptor sets are examined. Typically
* width has the value returned by getdtablesize for the
* maximum number of file descriptors. On return, select
* replaces the given descriptor sets with subsets consisting
* of those descriptors that are ready for the requested opera-
* tion. The total number of ready descriptors in all the sets
* is returned.
*
* If timeout is not a NULL pointer, it specifies a maximum
* interval to wait for the selection to complete. If timeout
* is a NULL pointer, the select blocks indefinitely. To
* effect a poll, the timeout argument should be a non-NULL
* pointer, pointing to a zero-valued timeval structure.
*
* Any of readfds, writefds, and exceptfds may be given as NULL
* pointers if no descriptors are of interest.
*
* Using select to open a socket for reading is analogous to
* performing an accept call.
*
* select() returns the number of ready descriptors that are
* contained in the descriptor sets, or -1 if an error
* occurred. If the time limit expires then select() returns
* 0. If select() returns with an error the descriptor sets
* will be unmodified.
*/
int s_select(
Int4 width,
fd_set *readfds,
fd_set *writefds,
fd_set *exceptfds,
struct timeval *timeout)
{
SocketPtr sp;
long count;
int s;
long starttime, waittime;
fd_set rd, wd, ed;
int errorHappened;
#if SOCK_DEBUG >= 2
dprintf("select: socket: width %d\n",width);
#endif
if (!goodptr(timeout) && timeout != NULL)
return(sock_err(EFAULT));
if (!goodptr(readfds) && readfds != NULL)
return(sock_err(EFAULT));
if (!goodptr(writefds) && writefds != NULL)
return(sock_err(EFAULT));
if (!goodptr(exceptfds) && exceptfds != NULL)
return(sock_err(EFAULT));
#if SOCK_DEBUG >= 3
dprintf("select: timeout %d sec, read %08x write %08x except %08x\n",
(timeout ? timeout->tv_sec : 99999),
(readfds!=NULL ? *readfds : 0L),
(writefds!=NULL ? *writefds : 0L),
(exceptfds!=NULL ? *exceptfds : 0L));
#endif
if (width > NUM_SOCKETS) /* for now..xxx. */
width = NUM_SOCKETS;
count = 0;
FD_ZERO(&rd);
FD_ZERO(&wd);
FD_ZERO(&ed);
if (timeout)
{
waittime = TVTOTICK(timeout->tv_sec,timeout->tv_usec);
starttime = TickCount();
}
#if SOCK_DEBUG >= 5
dprintf(" starttime = %d(tics); waittime = %d\n",starttime, waittime);
#endif
do
{
for (s = 0 , sp = sockets ; s < width ; ++s, ++sp)
{
if (is_used(sp))
{
errorHappened = 0;
/* Check if there is data or connection available. */
if (readfds && FD_ISSET(s,readfds))
{
switch(sp->protocol)
{
case IPPROTO_UDP:
switch (sock_udp_can_recv(sp))
{
case 1:
FD_SET(s,&rd);
++count;
break;
case -1:
errorHappened = 1;
break;
}
break;
case IPPROTO_TCP:
/* Must exit if stream is dead to avoid eternal lock up */
if (sock_tcp_can_read(sp) ) {
FD_SET(s,&rd);
++count;
}
break;
}
}
if (writefds && FD_ISSET(s,writefds))
{
switch(sp->protocol)
{
case IPPROTO_UDP:
switch (sock_udp_can_send(sp))
{
case 1:
FD_SET(s,&wd);
++count;
break;
case -1:
errorHappened = 1;
break;
}
break;
case IPPROTO_TCP:
if (sock_tcp_can_write(sp))
{
FD_SET(s,&wd);
++count;
}
break;
}
}
if (exceptfds && FD_ISSET(s,exceptfds))
{
if (errorHappened)
{
FD_SET(s,&ed);
++count;
}
}
}
}
SPIN(false,SP_SELECT,0L)
}
while(count == 0 &&(timeout == 0 || TickCount() - starttime < waittime));
if (readfds)
*readfds = rd;
if (writefds)
*writefds = wd;
if (exceptfds)
*exceptfds = ed;
#if SOCK_DEBUG >= 5
dprintf(" elapsed = %d(tics) count %d, read %08x write %08x except %08x\n",
TickCount()-starttime,count,
(readfds!=NULL ? *readfds : 0L),
(writefds!=NULL ? *writefds : 0L),
(exceptfds!=NULL ? *exceptfds : 0L));
#endif
return(count);
}
/*
* s_getdtablesize()
*
* The entries in the socket descriptor table are numbered with small
* integers starting at 0. getdtablesize returns the size of the
* descriptor table.
*/
int s_getdtablesize()
{
return(NUM_SOCKETS);
}
/*
* s_getsockname(s, name, namelen)
*
* getsockname returns the current name for the socket s.
* Namelen should be initialized to
* indicate the amount of space pointed to by name. On return
* it contains the actual size of the name returned (in bytes).
*
* A 0 is returned if the call succeeds, -1 if it fails.
*/
int s_getsockname(
Int4 s,
struct sockaddr *name,
Int4 *namelen)
{
#if SOCK_DEBUG >= 3
dprintf("GETSOCKNAME: %d\n", s);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
if (! goodptr(name))
return(sock_err(EFAULT));
if (*namelen < 0)
return(sock_err(EINVAL));
sock_copy_addr(&sockets[s].sa, name, namelen);
return(0);
}
/*
* s_getpeername(s, name, namelen)
*
* getpeername returns the name of the peer connected to socket s.
*
* The int pointed to by the namelen parameter
* should be initialized to indicate the amount of space
* pointed to by name. On return it contains the actual size
* of the name returned (in bytes). The name is truncated if
* the buffer provided is too small.
*
* A 0 is returned if the call succeeds, -1 if it fails.
*/
int s_getpeername(
Int4 s,
struct sockaddr *name,
Int4 *namelen)
{
SocketPtr sp;
#if SOCK_DEBUG >= 2
dprintf("getpeername: socket %d\n", s);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
sp = sockets+s;
if (! is_used (sp))
return (sock_err (EBADF));
if (! goodptr(name))
return(sock_err(EFAULT));
if (*namelen < 0)
return(sock_err(EINVAL));
if (sp->sstate != SOCK_STATE_CONNECTED)
return(sock_err(ENOTCONN));
sock_copy_addr(&sockets[s].peer, name, namelen);
return(0);
}
/*
* s_shutdown(s, how)
*
* shutdown call causes all or part of a full-duplex
* connection on the socket s to be shut down. If
* how is 0, then further receives will be disallowed. If how
* is 1, then further sends will be disallowed. If how is 2,
* then further sends and receives will be disallowed.
*
* A 0 is returned if the call succeeds, -1 if it fails.
*/
int s_shutdown(
Int4 s,
Int4 how)
{
SocketPtr sp;
#if SOCK_DEBUG >= 2
dprintf("shutdown: shutdown %d\n", s);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
sp = sockets+s;
switch(how)
{
case 0 :
sp->status |= SOCK_STATUS_NOREAD;
break;
case 1 :
sp->status |= SOCK_STATUS_NOWRITE;
break;
case 2 :
sp->status |= SOCK_STATUS_NOREAD | SOCK_STATUS_NOWRITE;
break;
default :
return(sock_err(EINVAL));
}
return(0);
}
/*
* fcntl() operates on the socket s according to the order in cmd:
*
* F_DUPFD Like Dup. Returns a new descriptor which refers to the
* same MacTCP stream as s and has the same descriptor
* status.
*
* F_GETFL returns the descriptor status flags for s. The only
* flag supported is FNDELAY for non-blocking i/o.
*
* F_SETFL sets descriptor status flags for s. The only
* flag supported is FNDELAY for non-blocking i/o.
*
* A dup or F_DUPFD operation copies the descriptor status flags
* maintained by F_SETFL, but once the copy is done, the two are
* disjoint. THIS IS DIFFERENT FROM UNIX.
*
* Upon successful completion, the value returned depends on
* cmd as follows:
* F_DUPFD A new descriptor.
* F_GETFL Value of flags.
* F_SETFL 0.
*
* On error, a value of -1 is returned and errno is set to indicate
* the error.
*
* EBADF s is not a valid open descriptor.
*
* EMFILE cmd is F_DUPFD and socket descriptor table is full.
*
* EINVAL cmd is F_DUPFD and arg is negative or
* greater than the maximum allowable
* number (see getdtablesize).
*/
int s_fcntl(
Int4 s,
/* unsigned Int4 cmd,*/
unsigned long cmd,
Int4 arg)
{
#if SOCK_DEBUG >= 2
dprintf("s_fcntl: %d\n", s);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
switch(cmd)
{
/*
* Duplicate a socket.
*/
case F_DUPFD :
{
int s1;
if (arg < 0 || arg >= NUM_SOCKETS)
return(sock_err(EINVAL));
s1 = sock_free_fd(arg);
if (s1 == -1)
return(sock_err(EMFILE));
sock_dup_fd(s,s1);
return(s1);
}
/*
* Get socket status. This is like getsockopt().
* Only supported descriptor status is FNDELAY.
*/
case F_GETFL :
{
SocketPtr sp;
sp = sockets+s;
if (sp->nonblocking)
return(FNDELAY);
else
return(0);
}
/*
* Set socket status. This is like setsockopt().
* Only supported descriptor status is FNDELAY.
*/
case F_SETFL :
{
SocketPtr sp;
sp = sockets+s;
if (arg & FNDELAY)
sp->nonblocking = true;
else
sp->nonblocking = false;
return(0);
}
}
return(0);
}
/*
* dup(s)
*
* dup2(s, news)
*
* dup() duplicates an existing socket descriptor. The argu-
* ment s is a small non-negative integer index in the per-
* process descriptor table. The value must be less than the
* size of the table, which is returned by getdtablesize(2).
* The new descriptor returned by the call is the lowest num-
* bered descriptor that is not currently in use by the pro-
* cess.
*
* In the second form of the call, the value of the new
* descriptor desired is specified. If that descriptor is
* already in use, the descriptor is first deallocated as if a
* close(2) call had been done first.
*
* The value -1 is returned if an error occurs in either call.
* The external variable errno indicates the cause of the
* error.
*
* EBADF s or news is not a valid socket
* descriptor.
*
* EMFILE Too many descriptors are active.
*/
int s_dup(
Int4 s)
{
return(s_fcntl(s, F_DUPFD, 0));
}
int s_dup2(
Int4 s,
Int4 s1)
{
if (! sock_good_fd(s))
return(sock_err(EBADF));
if (s1 < 0 || s1 >= NUM_SOCKETS)
return(sock_err(EBADF));
if (is_used(sockets+s1))
{
if (s_close(s1) == -1)
return(-1);
}
sock_dup_fd(s,s1);
return(s1);
}
/*
* s_Ioctl()
*/
int s_ioctl(
Int4 d,
Int4 request,
Int4 *argp)
{
struct ifreq *ifr;
TCPiopb *tpb;
SocketPtr sp;
int size;
#if SOCK_DEBUG >= 2
dprintf("s_ioctl: %d, request %d\n", d,request);
#endif
if (! sock_good_fd(d))
return(sock_err(EBADF));
sp = sockets+d;
/*
* Interpret high order word to find amount of data to be copied
* to/from the user's address space.
*/
size =(request &~(IOC_INOUT | IOC_VOID)) >> 16;
/*
* Zero the buffer on the stack so the user gets back something deterministic.
*/
if ((request & IOC_OUT) && size)
bzero((Ptr)argp, size);
ifr =(struct ifreq *)argp;
switch(request)
{
/* Non-blocking I/O */
case FIONBIO:
sp->nonblocking = *(Boolean *)argp;
return(0);
/* Number of bytes on input Q */
case FIONREAD:
tpb = (TCPiopb*)sock_fetch_pb(sp);
sp->dataavail = xTCPBytesUnread(sp);
*(int *)argp=sp->dataavail;
return 0;
#ifdef IOCTL_LATER
/*
* Get interface list. Pass in buffer and buffer length.
* Returns list of length one of ifreq's
*/
case SIOCGIFCONF:
{
struct ifconf *ifc =(struct ifconf *)argp;
struct ifreq *req = ifc->ifc_req;
int reqlen;
struct sockaddr_in *addr;
/*
* Fill in req fields for the IF's name and local IP addr.
*/
strncpy(req->ifr_name, myIFName, IFNAMSIZ);
addr =(struct sockaddr_in *)&req->ifr_addr;
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = myIPAddress;
addr->sin_port = 0;
bzero(addr->sin_zero, sizeof(addr->sin_zero));
ifc->ifc_len = sizeof(*req);
return(0);
}
/*
* Returns MTU of specified IF.
*/
case SIOCGIFMTU:
{
/* don't check IF specification - we only have one anyway */
*(int *)ifr->ifr_data= xMaxMTU();
return(0);
}
/*
* Returns local IP Address of IF
*/
case SIOCGIFADDR:
{
struct sockaddr_in *addr;
/* don't check IF specification - we only have one anyway */
addr = &ifr->ifr_addr;
addr->sin_addr.s_addr = xIPAddr();
addr->sin_family = AF_INET;
return(0);
}
case SIOCGIFDSTADDR: /* For point to point, which we don't support */
return(sock_err(EINVAL));
case SIOCGIFFLAGS: /* Returns IF flags(none yet) */
ifr->ifr_flags = 0;
return(0);
/*
* Return broadcast address - net address plus all ones in host part
*/
case SIOCGIFBRDADDR:
{
struct sockaddr_in *addr;
/* don't check IF specification */
/* we only have one and its broadcast */
addr = &ifr->ifr_addr;
addr->sin_addr.s_addr = xIPAddr() | ~xNetMask();
return(0);
}
#endif IOCTL_LATER
default :
return(sock_err(EOPNOTSUPP));
}
}
/*
* s_setsockopt()
*
* Set socket options. None implemented. In Unix there are...
*
* SO_REUSEADDR toggle local address reuse
* SO_KEEPALIVE toggle keep connections alive
* SO_DONTROUTE toggle routing bypass for outgoing
* messages
* SO_LINGER linger on close if data present
* SO_BROADCAST toggle permission to transmit
* broadcast messages
* SO_OOBINLINE toggle reception of out-of-band
* data in band
* SO_SNDBUF set buffer size for output
* SO_RCVBUF set buffer size for input
* SO_TYPE get the type of the socket (get
* only)
* SO_ERROR get and clear error on the socket
* (get only)
*/
int s_setsockopt(
Int4 s,
Int4 level,
Int4 optname,
char *optval,
Int4 optlen)
{
#pragma unused(optval)
#pragma unused(optlen)
SocketPtr sp;
#if SOCK_DEBUG >= 3
dprintf("SETSOCKOPT: socket: %d option: %d \n", s,optname);
#endif
if (! sock_good_fd(s))
return(sock_err(EBADF));
sp = sockets+s;
/*
* demultiplex to socket option handlers at other protocol levels.(None
* supported yet).
*/
switch(level)
{
case SOL_SOCKET : /* socket level option */
switch(optname)
{
default :
return(0);
}
break;
case IPPROTO_TCP:
switch(optname)
{
default:
return(0);
}
break;
default :
return(sock_err(ENOPROTOOPT));
}
return(0);
}
/*
* s_setspin() - define a routine to be called repeatedly when
* socket routines are blocked (ie. spinning)
*
* pass a NULL pointer to turn off a previously
* defined spin routine.
*/
int s_setspin(
SpinFn routine)
{
if (routine == NULL || goodptr(routine))
{
spinroutine = routine;
return(0);
}
else
return(sock_err(EFAULT));
}
/*
* s_getspin() - returns current spinroutine
*/
SpinFn s_getspin()
{
return (spinroutine);
}
int defaultSpin(spin_msg msg,long param)
{
#pragma unused (msg,param)
EventRecord evrec;
WaitNextEvent(0, &evrec, 1 /* ticks */, NULL);
return 0; /* return non-zero to exit current routine */
}